// index.js
const fs = require('fs');
const os = require('os');
const path = require('path');
const util = require('util');
const crypto = require('crypto');
const axios = require('axios');
const si = require('systeminformation');
const { exec } = require('child_process');
const execAsync = util.promisify(exec);

// Logging Setup
const logFilePath = path.join(__dirname, 'agent.log');
const maxLogSize = 10 * 1024 * 1024;
function rotateLog() {
  if (fs.existsSync(logFilePath) && fs.statSync(logFilePath).size > maxLogSize) {
    const oldPath = `${logFilePath}.1`;
    if (fs.existsSync(oldPath)) fs.unlinkSync(oldPath);
    fs.renameSync(logFilePath, oldPath);
  }
}
function log(message) {
  const time = new Date().toISOString();
  const msg = `[${time}] ${message}\n`;
  rotateLog();
  fs.appendFileSync(logFilePath, msg);
}
console.log = log;
console.error = log;

// Configuration
const configPath = './config.json';
if (!fs.existsSync(configPath)) {
  fs.writeFileSync(configPath, JSON.stringify({
    RMM_Enrolled: false,
    RMM_ServerURL: "https://api.nexifyit.cloud",
    RMM_EnrollmentKey: ""
  }, null, 2));
}
let config = require(configPath);
function saveConfig() { fs.writeFileSync(configPath, JSON.stringify(config, null, 2)); }
function getAuthHeaders() {
  return {
    headers: { Authorization: `Bearer ${config.RMM_ApiKey}` }
  };
}

// Version
let AgentVersion = "0.0.0";
try { AgentVersion = fs.readFileSync('./version.txt', 'utf8').trim(); } catch (_) {}

// Enrollment
async function enroll() {
  try {
    const hostname = os.hostname();
    const platform = os.platform();
    const arch = os.arch();
    const release = os.release();

    const response = await axios.post(`${config.RMM_ServerURL}/v1/app/rmm/agent/enroll`, {
      hostname, platform, arch, release,
      enrollmentKey: config.RMM_EnrollmentKey
    });

    config.RMM_ApiKey = response.data.apiKey;
    config.RMM_Enrolled = true;
    saveConfig();
    log("Enrolled successfully.");
  } catch (err) {
    log("Enrollment failed: " + err.message);
  }
}

// File Integrity Monitoring
function hashFile(filePath) {
  return new Promise((resolve, reject) => {
    const hash = crypto.createHash('sha256');
    const stream = fs.createReadStream(filePath);
    stream.on('data', data => hash.update(data));
    stream.on('end', () => resolve(hash.digest('hex')));
    stream.on('error', reject);
  });
}
async function checkFileIntegrity(files, knownHashes = {}) {
  const results = {};
  for (const file of files) {
    try {
      const hash = await hashFile(file);
      const orig = knownHashes[file] || null;
      results[file] = { current: hash, original: orig, changed: orig && hash !== orig };
    } catch (err) {
      results[file] = { error: err.message };
    }
  }
  return results;
}

// System Inventory Functions
async function getSystemDetails() {
  const sys = await si.system();
  const osInfo = await si.osInfo();
  return { system: sys, os: osInfo };
}
async function getCpuAndMemory() {
  const cpu = await si.cpu();
  const mem = await si.mem();
  return { cpu, memory: mem };
}
async function getDiskInfo() {
  const disks = await si.diskLayout();
  const fsSize = await si.fsSize();
  return { disks, fileSystems: fsSize };
}
async function getNetworkInfo() {
  const interfaces = await si.networkInterfaces();
  const stats = await si.networkStats();
  return { interfaces, networkStats: stats };
}
async function getServiceStatuses() {
  const services = await si.services('*');
  return { services };
}
async function getInstalledPackages() {
  const platform = os.platform();
  if (platform === 'linux') {
    const { stdout } = await execAsync('dpkg-query -W -f=\'${binary:Package}\n\'');
    return { packages: stdout.split('\n') };
  } else if (platform === 'win32') {
    const { stdout } = await execAsync('powershell "Get-WmiObject -Class Win32_Product | Select-Object -ExpandProperty Name"');
    return { packages: stdout.split('\n') };
  }
  return { packages: [] };
}
async function getSystemUpdates() {
  try {
    const platform = os.platform();
    if (platform === 'linux') {
      const { stdout } = await execAsync('apt list --upgradable');
      return { updates: stdout.split('\n') };
    } else if (platform === 'win32') {
      const { stdout } = await execAsync('powershell "Get-WindowsUpdate"');
      return { updates: stdout.split('\n') };
    }
    return { updates: [] };
  } catch {
    return { updates: [] };
  }
}
async function getUtilization() {
  const load = await si.currentLoad();
  const mem = await si.mem();
  return { load, memoryUsage: mem.used / mem.total };
}
async function getRunningProcesses() {
  const procs = await si.processes();
  return procs.list.map(p => ({
    pid: p.pid, name: p.name, cmd: p.command,
    cpu: p.pcpu, memory: p.pmem, user: p.user
  }));
}
async function getOpenPorts() {
  try {
    const cmd = os.platform() === 'win32' ? 'netstat -ano' : 'ss -tuln';
    const { stdout } = await execAsync(cmd);
    return stdout;
  } catch (err) {
    return `Error: ${err.message}`;
  }
}
async function getSecuritySettings() {
  const checks = {};
  const platform = os.platform();
  if (platform === 'linux') {
    try {
      const sshConf = fs.readFileSync('/etc/ssh/sshd_config', 'utf8');
      checks.permitRootLogin = /PermitRootLogin\s+no/.test(sshConf);
    } catch { checks.permitRootLogin = null; }
    try {
      const { stdout } = await execAsync('ufw status');
      checks.firewallEnabled = stdout.includes('active');
    } catch { checks.firewallEnabled = false; }
  } else if (platform === 'win32') {
    try {
      const { stdout } = await execAsync(`powershell "Get-NetFirewallProfile | Select-Object Name,Enabled | ConvertTo-Json"`);
      const profiles = JSON.parse(stdout);
      checks.firewallEnabled = profiles.every(p => p.Enabled);
    } catch { checks.firewallEnabled = false; }
  }
  return checks;
}
async function detectAnomalies() {
  const procs = await getRunningProcesses();
  const highCpu = procs.filter(p => p.cpu > 50);
  const suspicious = procs.filter(p => p.cmd.match(/(netcat|nmap|meterpreter|powershell|cmd|bash)/i));
  return { highCpu, suspicious };
}
function correlateToMITRE(processes) {
  const rules = [
    { tactic: 'Execution', technique: 'T1059', match: /powershell|cmd\.exe|bash/i },
    { tactic: 'Persistence', technique: 'T1547', match: /autoruns|registry/i }
  ];
  return processes
    .filter(p => rules.some(r => r.match.test(p.cmd)))
    .map(p => {
      const r = rules.find(r => r.match.test(p.cmd));
      return { pid: p.pid, command: p.cmd, tactic: r.tactic, technique: r.technique };
    });
}

// Full Inventory Aggregator
async function getFullSystemInventory() {
  const [
    sys, cpuMem, disks, net, services,
    packages, updates, utilization,
    processes, ports, security, anomalies
  ] = await Promise.all([
    getSystemDetails(),
    getCpuAndMemory(),
    getDiskInfo(),
    getNetworkInfo(),
    getServiceStatuses(),
    getInstalledPackages(),
    getSystemUpdates(),
    getUtilization(),
    getRunningProcesses(),
    getOpenPorts(),
    getSecuritySettings(),
    detectAnomalies()
  ]);

  const criticalFiles = os.platform() === 'linux'
    ? ['/etc/passwd', '/etc/ssh/sshd_config']
    : ['C:\\Windows\\System32\\drivers\\etc\\hosts'];
  const fim = await checkFileIntegrity(criticalFiles, config.RMM_FIM_Hashes || {});
  config.RMM_FIM_Hashes = fim;
  saveConfig();

  return {
    ...sys, ...cpuMem, ...disks, ...net, ...services,
    ...packages, ...updates, ...utilization,
    processes, openPorts: ports,
    securityConfig: security,
    anomalies, fim,
    mitreHits: correlateToMITRE(processes),
    timestamp: new Date().toISOString()
  };
}

// Data Poster
async function postInventoryData() {
  try {
    const data = await getFullSystemInventory();
    data.version = AgentVersion;
    await axios.post(`${config.RMM_ServerURL}/v1/app/rmm/agent/data`, data, getAuthHeaders());
    log("Posted system data.");
  } catch (err) {
    log("Failed to post data: " + err.message);
  }
}

// Heartbeat
async function sendHeartbeat() {
  try {
    await axios.post(`${config.RMM_ServerURL}/v1/app/rmm/agent/heartbeat`, {}, getAuthHeaders());
    log("Heartbeat sent.");
  } catch (err) {
    log("Heartbeat failed: " + err.message);
  }
}

// Poll Commands
async function pollCommands() {
  try {
    const res = await axios.get(`${config.RMM_ServerURL}/v1/app/rmm/agent/commands`, getAuthHeaders());
    for (const cmd of res.data) {
      const { stdout, stderr } = await execAsync(cmd.command);
      await axios.post(`${config.RMM_ServerURL}/v1/app/rmm/agent/command-result`, {
        id: cmd.id,
        output: stdout + stderr
      }, getAuthHeaders());
      log(`Command ${cmd.id} executed.`);
    }
  } catch (err) {
    log("Polling commands failed: " + err.message);
  }
}

// Main
async function main() {
  if (!config.RMM_Enrolled) await enroll();
  await sendHeartbeat();
  await pollCommands();
  await postInventoryData();
  setInterval(sendHeartbeat, 5 * 60 * 1000);
  setInterval(pollCommands, 60 * 1000);
  setInterval(postInventoryData, 15 * 60 * 1000);
}

main();
